package game;

import java.util.*;

import main.Main;

import org.eclipse.swt.events.*;

import javax.vecmath.*;
import sceneGraph.*;

public class Game {

	private Scene scene;
	private Player player;
	private int screenWidth;
	private int screenHeight;
	private int lastGameStatus = 0;

	private int levelScore;
	private int livesLeft;
	private int portalScore = 100;

	private TextObject scoreTxt;
	private TextObject livesTxt;
	private TextObject userMsgTxt;
	private TextObject lastGameMsgTxt;
	private int userMsgCounter = 0;
	private int userMsgTimeout = 25;
	private int lastGameMsgCounter = 0;
	private int lastGameMsgTimeout = 150;

	private Set<Integer> keysPressed;

	public Game() {
		scene = new Scene();
		keysPressed = new HashSet<Integer>();
		init();
	}

	public Scene updateScene() {
		AbstractPlanet curPlanet = scene.getCurrentPlanet();
		Player player = curPlanet.getPlayer();

		handleKeys();

		// Clear user messages
		if (userMsgCounter++ > userMsgTimeout) {
			updateUserMsg("", 10);
			userMsgCounter = 0;
		}
		if (lastGameMsgCounter++ > lastGameMsgTimeout) {
			updateLastGameMsg("");
		}

		// Start game loop
		int beforeScore = levelScore;

		// Let the planets orbit, and animate player
		for (AbstractPlanet p : scene.getPlanetsAndMoons()) {
			p.incrementAngle();
		}
		for (Planet p : scene.getPlanets()) {
			p.recalcAbsPosition();
		}
		scene.refreshPlayer();

		// Move moving objects
		for (AbstractPlanetObject obj : curPlanet.getPlanetObjects()) {
			if (obj instanceof IEnemy) {
				IEnemy enemy = (IEnemy) obj;
				enemy.advanceTowards(player.getPosition());
			}
			if (obj instanceof Portal) {
				Portal portal = (Portal) obj;
				Vector3f newLeftDirection = new Vector3f(portal.getLeftDirection());
				newLeftDirection.scaleAdd(10, portal.getDirection());
				portal.setVectors(portal.getPosition(), portal.getUpDirection(), newLeftDirection);
			}
		}
		curPlanet.repositionPlanetObjects();

		// Check if player intersected something
		AbstractPlanetObject intersectedObject = scene.getIntersectedObject();

		// Portal
		if (intersectedObject instanceof Portal) {
			Portal portal = (Portal) intersectedObject;
			if (!scene.handlePortal(portal)) {
				updateUserMsg("Portals still deactivated! Need " + portalScore + " score", 10);
			} else {
				if (portal.getDestination() != null) {
					scene.activatePortals(false);
					levelScore = 0;
				} else {
					lastGameStatus = 1;
					restartGame();
				}
			}
		}
		// Trophy1
		else if (intersectedObject instanceof Trophy1) {
			Trophy1 trophy1 = (Trophy1) intersectedObject;
			curPlanet.removePlanetObject(trophy1);
			levelScore += trophy1.getScore();
			updateUserMsg("+" + trophy1.getScore(), 50);
		}
		// Trophy2
		else if (intersectedObject instanceof Trophy2) {
			Trophy2 trophy2 = (Trophy2) intersectedObject;
			curPlanet.removePlanetObject(trophy2);
			updateUserMsg("+ 1 Live!", 50);
			livesLeft++;
		}
		// IEnemy
		else if (intersectedObject instanceof IEnemy) {
			curPlanet.removePlanetObject(intersectedObject);
			if (scene.getPlayerHeight() == 0) {
				updateUserMsg("- 1 Live!", 50);
				livesLeft--;
			} else {
				updateUserMsg("Enemy Killed! +20", 50);
				levelScore += 20;
			}
		}

		scoreTxt.setText("Score: " + levelScore);
		livesTxt.setText("Lives: " + livesLeft);

		// Activate portals to next stage
		if (levelScore >= portalScore && levelScore != beforeScore) {
			updateUserMsg("Portals Activated!", 50);
			scene.activatePortals(true);
		}
		if (livesLeft < 0) {
			lastGameStatus = -1;
			restartGame();
		}

		return scene;
	}

	private void generateLevel(AbstractPlanet p, AbstractPlanet nextPlanet, int numTrophies1, int numTrophies2, int numEnemy1, int numEnemy2, int numEnemy3) {
		// Add trophies
		for (int i = 0; i < numTrophies1; i++) {
			Trophy1 trophy1 = new Trophy1(new Point3f((float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1), 10);
			p.addPlanetObject(trophy1);
		}
		for (int i = 0; i < numTrophies2; i++) {
			Trophy2 trophy2 = new Trophy2(new Point3f((float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1));
			p.addPlanetObject(trophy2);
		}

		// Add enemies
		for (int i = 0; i < numEnemy1; i++) {
			Enemy1 enemy1 = new Enemy1(new Point3f((float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1));
			p.addPlanetObject(enemy1);
		}
		for (int i = 0; i < numEnemy2; i++) {
			Enemy2 enemy2 = new Enemy2(new Point3f((float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1));
			p.addPlanetObject(enemy2);
		}
		for (int i = 0; i < numEnemy3; i++) {
			Enemy3 enemy3 = new Enemy3(new Point3f((float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1, (float) Math.random() * 2 - 1));
			p.addPlanetObject(enemy3);
		}

		// Add portal (if exists)
		Portal portal = new Portal(new Point3f(0, 1, 0), 0.5f, nextPlanet, new Color3f(0.5f, 0.3f, 0.8f), new Color3f(0.5f, 0.9f, 0.1f),
				new Color3f(1f, 1f, 1f), 10);
		p.addPlanetObject(portal);
	}

	public void init() {
		// Create player
		String mesh_dir = "/images/other/";
		player = new Player(0.5f, mesh_dir + "bomb.off", 1f, new Color3f(0.2f, 0.0f, 0.8f), new Color3f(0.2f, 0.2f, 0.2f), new Color3f(0f, 0f, 0.8f), 0.1f);

		// Create the planets & moons
		String p1_dir = "/images/planet1/";
		String p2_dir = "/images/planet2/";
		String p1_m1_dir = "/images/planet3/";
		String p1_m1_m1_dir = "/images/planet4/";

		Planet p1 = new Planet(10, new Point3f(0, 0, 0), new Color3f(0.83f, 0.65f, 0.44f), new Color3f(0f, 0f, 0f), new Color3f(0.11f, 0.08f, 0.06f), 20,
				false, new Vector3f(0, 1, 0), 0, new Point3f(0, 0, 0), p1_dir + "texture.png", p1_dir + "heightmap.png");

		Planet p2 = new Planet(15, new Point3f(70, 70, 0), new Color3f(0.7f, 0.7f, 0.7f), new Color3f(0f, 0f, 0f), new Color3f(0.09f, 0.09f, 0.09f), 20, true,
				new Vector3f(1, -1, 0), 3, new Point3f(-20, -20, 0), p2_dir + "texture.png", p2_dir + "heightmap.png");

		Moon p1_m1 = new Moon(9, new Point3f(50, 50, 0), new Color3f(0.5f, 1f, 0.5f), new Color3f(0f, 0f, 0f), new Color3f(0.05f, 0.1f, 0.05f), 20, false,
				new Vector3f(-1, 1, 0), 0.5f, p1_m1_dir + "texture.png", p1_m1_dir + "heightmap.png");
		p1.addMoon(p1_m1);

		Moon p1_m1_m1 = new Moon(10, new Point3f(30, 0, 0), new Color3f(1f, 1f, 0.1f), new Color3f(0f, 0f, 0f), new Color3f(0.1f, 0.1f, 0.01f), 20, true,
				new Vector3f(0, 1, 0), 1f, p1_m1_m1_dir + "texture.png", p1_m1_m1_dir + "heightmap.png");
		p1_m1.addMoon(p1_m1_m1);

		scene.addPlanet(p1);
		scene.addPlanet(p2);
		scene.addMoon(p1_m1);
		scene.addMoon(p1_m1_m1);

		// Create text boxes for score & msgs
		scoreTxt = new TextObject("Level Score: " + levelScore, new Point2f(10, 10), new Color3f(1f, 0.5f, 0f));
		livesTxt = new TextObject("Lives: " + livesLeft, new Point2f(10, 30), new Color3f(1f, 0.0f, 0f));
		userMsgTxt = new TextObject("", new Point2f(10, 50), new Color3f(1f, 0.0f, 0f));
		lastGameMsgTxt = new TextObject("Good Luck!", new Point2f(0, 0), new Color3f(1f, 0.0f, 0f));
		scene.addTextObject(scoreTxt);
		scene.addTextObject(livesTxt);
		scene.addTextObject(userMsgTxt);
		scene.addTextObject(lastGameMsgTxt);

		restartGame();
	}

	private void restartGame() {
		Main.animator.stop();

		// Reset game stats
		levelScore = 0;
		livesLeft = 5;

		// Remove all older elements from planets
		scene.activatePortals(false);
		for (AbstractPlanet p : scene.getPlanetsAndMoons()) {
			p.getPlanetObjects().clear();
		}
		printLastGameMessage();

		// Generate the planets
		List<AbstractPlanet> levels = scene.getPlanetsAndMoons();
		AbstractPlanet p1 = levels.get(0);
		AbstractPlanet p2 = levels.get(1);
		AbstractPlanet p1_m1 = levels.get(2);
		AbstractPlanet p1_m1_m1 = levels.get(3);
		generateLevel(p1, p2, 20, 7, 10, 5, 2);
		generateLevel(p2, p1_m1, 15, 3, 15, 5, 2);
		generateLevel(p1_m1, p1_m1_m1, 15, 0, 15, 10, 2);
		generateLevel(p1_m1_m1, null, 15, 0, 15, 1, 10);

		// Set the player to p1
		scene.setPlayer(player, p1);

		Main.animator.start();
	}

	private void updateUserMsg(String text, int timeout) {
		userMsgTxt.setText(text);
		userMsgTimeout = timeout;
		userMsgCounter = 0;
	}

	private void updateLastGameMsg(String text) {
		lastGameMsgTxt.setText(text);
		lastGameMsgCounter = 0;
	}

	private void printLastGameMessage() {
		if (lastGameStatus == 1) {
			updateLastGameMsg("You've WON! Game Restarted!");
		} else if (lastGameStatus == -1) {
			updateLastGameMsg("You've LOST! Game Restarted!");
		}
	}

	public void handleKeyPressed(KeyEvent k) {
		if (k.keyCode == 109) { // 'm'
			scene.toggleMapMode();
		}
		if (k.keyCode == 32) { // 'SPACE'
			scene.playerJump();
		}
		keysPressed.add(k.keyCode);
	}

	public void handleKeyReleased(KeyEvent k) {
		keysPressed.remove(k.keyCode);
	}

	private void handleKeys() {
		if (keysPressed.contains(16777217)) { // UP
			scene.moveForward();
		}
		if (keysPressed.contains(16777218)) { // DOWN
			scene.moveBackward();
		}
		if (keysPressed.contains(16777219)) { // LEFT
			scene.moveLeft();
		}
		if (keysPressed.contains(16777220)) { // RIGHT
			scene.moveRight();
		}
		if (keysPressed.contains(16777259) || keysPressed.contains(61)) { // PLUS
			scene.zoomIn();
		}
		if (keysPressed.contains(16777261) || keysPressed.contains(45)) { // MINUS
			scene.zoomOut();
		}
	}

	public void setEnvironment(int width, int height) {
		screenWidth = width;
		screenHeight = height;

		int index;
		if ((index = scene.getTextObjects().indexOf(lastGameMsgTxt)) != -1) {
			scene.getTextObjects().get(index).setPosition(new Point2f(Math.max(screenWidth / 2 - 50, 0), Math.max(screenHeight - 50, 0)));
		}
	}

}
